package xyz.xtt.equity.utils.pay;


import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * 准备待签名的数据
 * 参数排序：将所有需要参与签名的参数按字典顺序（ASCII码顺序）进行排序。
 * 过滤空值：去除值为空或未设置的参数。
 * 拼接键值对：将排序后的参数按照“key=value”的格式拼接成字符串
 *
 * @author zhangcj
 * @date 2025/2/7 9:48
 */
public class SignatureHelper {

    private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final Logger log = LoggerFactory.getLogger(SignatureHelper.class);

    /**
     * 验证时间戳的有效性
     *
     * @param timestamp 请求中的时间戳，格式为 "yyyy-MM-dd HH:mm:ss"
     * @return 是否有效
     */
    public static boolean validateTimestamp(String timestamp) {
        // 设置允许的最大时间差（例如5分钟）
        long maxAllowedTimeDifferenceMinutes = 10;

        try {
            // 将请求中的时间戳解析为 LocalDateTime
            LocalDateTime requestTimeLocal = LocalDateTime.parse(timestamp, TIMESTAMP_FORMATTER);

            // 将 LocalDateTime 转换为带有时区信息的 Instant
            Instant requestTime = requestTimeLocal.atZone(ZoneId.systemDefault()).toInstant();

            // 获取当前时间
            Instant currentTime = Instant.now();

            // 计算时间差（绝对值）
            long timeDifferenceMinutes = ChronoUnit.MINUTES.between(requestTime, currentTime);
            if (timeDifferenceMinutes < 0) {
                timeDifferenceMinutes = ChronoUnit.MINUTES.between(currentTime, requestTime);
            }

            // 判断时间差是否在允许范围内
            return timeDifferenceMinutes <= maxAllowedTimeDifferenceMinutes;
        } catch (Exception e) {
            // 处理时间格式解析错误
            log.error("Invalid timestamp format: " + e.getMessage());
            return false;
        }
    }

    /**
     * 准备待签名的数据
     *
     * @param obj 需要参与签名的实体对象
     * @return 拼接好的签名字符串
     */
    public static String prepareSignData(Object obj) throws IllegalAccessException {
        Map<String, Object> params = getAllFields(obj);
        return prepareSignData(params);
    }

    /**
     * 获取对象及其父类的所有字段及其值
     *
     * @param obj 需要获取字段的对象
     * @return 包含字段名和字段值的Map
     */
    private static Map<String, Object> getAllFields(Object obj) throws IllegalAccessException {
        Map<String, Object> result = new TreeMap<>();
        Class<?> currentClass = obj.getClass();

        while (currentClass != null) {
            Field[] fields = currentClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true); // 允许访问私有字段
                Object value = field.get(obj);
                if (value != null) {
                    result.put(field.getName(), value);
                }
            }
            currentClass = currentClass.getSuperclass(); // 获取父类
        }
        return result;
    }

    /**
     * 准备待签名的数据
     *
     * @param params 需要参与签名的参数集合
     * @return 拼接好的签名字符串
     */
    public static String prepareSignData(Map<String, Object> params) {
        // 过滤掉空值和未设置的参数
        Map<String, String> filteredParams = filterEmptyValues(params);

        // 将参数按字典顺序排序
        List<Map.Entry<String, String>> sortedEntries = sortParameters(filteredParams);

        // 拼接成“key=value”格式的字符串，并用&连接
        StringBuilder signDataBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedEntries) {
            signDataBuilder.append(entry.getKey())
                    .append("=")
                    .append(entry.getValue())
                    .append("&");
        }

        // 去除最后一个多余的&
        if (signDataBuilder.length() > 0 && signDataBuilder.charAt(signDataBuilder.length() - 1) == '&') {
            signDataBuilder.deleteCharAt(signDataBuilder.length() - 1);
        }

        return signDataBuilder.toString();
    }

    public static void main(String[] args) {
        String jsonStr = "{\"orderNo\":\"NFT_1896167204388802560\",\"thirdTradeNo\":null,\"paidTime\":null,\"amount\":0.01000,\"sign\":\"nX48orSm/UnUhwPzrvMiaEnfClLmWYbmUnz4UhXbXaEMMJFdHR8OMkNlJaN6JTfQvpCq5ewDKhT0XArFQg7i3xyQNnTkD0iNzYePII7fjPB9rC7Gk47T57gHW9JDJ3eVMexoHODZ8CxhU7MLOi4b+56rujCY53BURLMW+8ukMSbgKHUBuS+9x5anlWRlVtkC6Qhqnrc647NP/k20zDPDWvgn7lAsqjPXdAjyzZIA6Ddm8OxJeBrK77tTIYb3yUznCkixk/M/8YexCZJ/DOYsxUZwLpA0Ynz+DTYjl2rCTgJ4tleMaKwI61ijgxRcuLoDoR4qruo81mjiIUyuPhWrIw==\",\"status\":\"2\",\"msg\":\"nullnull\"}";
        cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
//        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        jsonObject.remove("sign");
//        Map<String, Object> params = jsonObject.getInnerMap();
        Map<String, Object> params = jsonObject.toBean(Map.class);
        String signData = SignatureHelper.prepareSignData(params);
        log.info("sign:{}",signData);
    }

    /**
     * 过滤掉空值和未设置的参数
     *
     * @param params 参数集合
     * @return 过滤后的参数集合
     */
    private static Map<String, String> filterEmptyValues(Map<String, Object> params) {
        log.info("filterEmptyValues params:{}", JSONUtil.toJsonStr(params));
        Map<String, String> filteredParams = new TreeMap<>();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            Object value = entry.getValue();
            if (value != null) {
                String stringValue;
                if(value instanceof BigDecimal){
                    stringValue = ((BigDecimal) value).stripTrailingZeros().toString();
                } else if (isObject(value)) {
                    // 如果是对象类型，则将其转换为JSON字符串并递归排序
                    stringValue = JSONObject.toJSONString(sortNestedObject(value));
                } else {
                    stringValue = value.toString();
                }
                if (!stringValue.trim().isEmpty()) {
                    filteredParams.put(entry.getKey(), stringValue);
                }
            }
        }
        return filteredParams;
    }

    /**
     * 判断是否是对象类型（非基本类型、字符串等）
     *
     * @param value 需要判断的对象
     * @return 是否是对象类型
     */
    private static boolean isObject(Object value) {
        return !(value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof Character);
    }

    /**
     * 对嵌套对象进行排序
     *
     * @param value 需要排序的对象
     * @return 排序后的对象
     */
    private static Object sortNestedObject(Object value) {
        if (value instanceof Map) {
            return sortMap((Map<?, ?>) value);
        } else if (value instanceof List) {
            return sortList((List<?>) value);
        } else if (value instanceof Collection) {
            return sortCollection((Collection<?>) value);
        } else if (isObject(value)) {
            return sortObject(value);
        } else if(value instanceof BigDecimal){
            BigDecimal valueBigDecimal = (BigDecimal)value;
            return valueBigDecimal.stripTrailingZeros().toString();
        }
        return value;
    }

    /**
     * 对Map进行排序
     *
     * @param map 需要排序的Map
     * @return 排序后的Map
     */
    private static Map<String, Object> sortMap(Map<?, ?> map) {
        Map<String, Object> sortedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (key instanceof String) {
                sortedMap.put((String) key, sortNestedObject(value));
            }else if(entry.getValue() instanceof BigDecimal){
                if(entry.getValue()!=null){
                    BigDecimal valueBigDecimal = (BigDecimal)entry.getValue();
                    sortedMap.put((String) key, valueBigDecimal.stripTrailingZeros().toString());
                }else{
                    sortedMap.put((String) key, sortNestedObject(entry.getValue()));
                }
            }
        }
        return sortedMap;
    }

    /**
     * 对List进行排序
     *
     * @param list 需要排序的List
     * @return 排序后的List
     */
    private static List<Object> sortList(List<?> list) {
        List<Object> sortedList = new ArrayList<>();
        for (Object item : list) {
            sortedList.add(sortNestedObject(item));
        }
        return sortedList;
    }

    /**
     * 对Collection进行排序
     *
     * @param collection 需要排序的Collection
     * @return 排序后的Collection
     */
    private static List<Object> sortCollection(Collection<?> collection) {
        List<Object> sortedList = new ArrayList<>();
        for (Object item : collection) {
            sortedList.add(sortNestedObject(item));
        }
        return sortedList;
    }

    /**
     * 对对象进行排序
     *
     * @param obj 需要排序的对象
     * @return 排序后的Map
     */
    private static Map<String, Object> sortObject(Object obj) {
        Map<String, Object> result = new TreeMap<>();
        try {
            Map<String, Object> fields = getAllFields(obj);
            for (Map.Entry<String, Object> entry : fields.entrySet()) {
                if(entry.getValue() instanceof BigDecimal){
                    if(entry.getValue()!=null){
                        BigDecimal value = (BigDecimal)entry.getValue();
                        result.put(entry.getKey(), value.stripTrailingZeros().toString());
                    }else{
                        result.put(entry.getKey(), sortNestedObject(entry.getValue()));
                    }
                }else {
                    result.put(entry.getKey(), sortNestedObject(entry.getValue()));
                }
            }
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Failed to access fields of the object", e);
        }
        return result;
    }

    /**
     * 将参数按字典顺序排序
     *
     * @param params 参数集合
     * @return 排序后的参数列表
     */
    private static List<Map.Entry<String, String>> sortParameters(Map<String, String> params) {
        List<Map.Entry<String, String>> entries = new ArrayList<>(params.entrySet());
        entries.sort(Map.Entry.comparingByKey());
        return entries;
    }

}
